/* Papastefanos Serafeim */
/* To programma tou pelath */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <upnp/ixml.h>
#include "custom.h"
#include "client.h"
#include "soaparse.h"
#include "soapcrt.h"
#include "low.h"
#include "secure.h"
#include "que.h"
#include "helpers.h"
#include "typechk.h"


int handleConnectionSSL(SSL *ssl); 
int handleConnection(SOCKET sock);

int doReboot = 0;

static int usage(void) {
	dprintf(2, "usage: ./prog ip port usessl(1/0)\n"
		   "if all args are omitted, autodetection is used\n"
		   "example: ./prog 192.168.1.1 5000 1\n");
	return 1;
}

int main(int argc, char** argv) {


//	IXML_Document *doc =createGetParameterNamesResponse(NULL, "", 1);
//	char *c = (char *)ixmlPrintNode( (IXML_Node*)doc );
//	puts(c);
//	exit(0);

	int listenport ;
	SSL_CTX *ctx;

	#ifdef _CWMP_WIN
	initWinsock();
	#endif

	listenport = getListeningPort();

	char addr[256];
	int port;
	int secure;
	if(argc == 1)
		getConnectionAddress(addr, &port, &secure);
	else if(argc != 4)
		return usage();
	else {
		port = atoi(argv[2]);
		strcpy(addr, argv[1]);
		secure = atoi(argv[3]);
	}

	if(secure!=0) {
		init_OpenSSL();
		seed_prng(); 
		ctx = setup_client_ctx();
	}


	PRINTD("Connecting to %s, port %d, while secure is %d.", addr, port, secure);

	if(secure==0) {
		SOCKET sock = makeConnection(addr, port);
		if(sock > 0) {
			handleConnection(sock);
		} else {
			printf("Cannot connect to %s:%d.", addr, port);
		}
	} else {
		SSL *ssl = makeConnectionSSL(addr, port, ctx);
		if(ssl != NULL) {
			handleConnectionSSL(ssl); 
		} else { // La9os
			printf("Cannot connect to %s:%d.", addr, port);
		}
	}


	// Now we start listening for connection-demand from the ACS 
	SOCKET lsd=createListeningSocket(listenport);
	if(lsd == 0) {
		printf("Cannot listen on %d", listenport);
		exit(0);
	}
	while(1) {
		PRINTD("\nWaiting for a connection from the ACS\n");
		SOCKET sd = acceptConnection(lsd);
		if(sd == 0) { // La9os
			continue ;
		}
		if(checkConnectionString(getConnectionString(), sd) == 0 ) {
			printf("Did not receive the correct connection string.");
			mySocketClose(sd);
			continue;
		}  else {
			PRINTD("Received the correct connection string, connecting to the acs server. \n");
			mySocketClose(sd);

			char addr[256];
			int port;
			int secure;
			getConnectionAddress(addr, &port, &secure);
			PRINTD("Connecting to %s, port %d, while secure is %d.", addr, port, secure);

			if(secure==0) {
				SOCKET sock = makeConnection(addr, port);
				if(sock > 0) {
					handleConnection(sock);
				} else {
					printf("Cannot connect to %s:%d.", addr, port);
				}
			} else {
				SSL *ssl = makeConnectionSSL(addr, port, ctx);
				if(ssl != NULL) {
					handleConnectionSSL(ssl); 
				} else { // La9os
					printf("Cannot connect to %s:%d.", addr, port);
				}
			}
		}

		if(doReboot==1) handleReboot();
	}

	SSL_CTX_free(ctx);


	return 0;
}


/* Epistrefei ena xml document me to inform request pou 9a steilei o
 * client mas ston ACS server. */
IXML_Document *createCPEInform(void) {
	// Arxika dhmiourgeitai to deviceId
	DeviceIdStruct *deviceId = getDeviceId();
	if(deviceId == NULL) {
		return NULL;
		PRINTD("Error while trying to get the device id.");
	}
	// Sth sunexeia o xronos
	DateTime *currentTime = getCurrentTime();
	if(currentTime == NULL) {
		return NULL;
		PRINTD("Error while trying to get current time.");
	}
	
	// To MaxEnvelopes kai to RetryCount
	int maxEnvelope=10; // Mporei na einai kai perissotero
	int retryCount=0;
	
	// H lista me tis parametrous pou prepei na stalloun sto inform
	ParameterValueList *parameterList = getInformParameterList();
	
	// H lista me ta events pou odhghsan sto connection
	EventList *event=newEventList(1);
	event->events[0].eventCode = strdup("6 CONNECTION REQUEST");

	// Telos, dhmiourgeitai ena mhnuma inform xwris header kai me ta stoixeia pou
	// oristhkan prohgoumenws
	IXML_Document *inform = createInform(NULL, *deviceId, event, maxEnvelope, *currentTime, retryCount, parameterList);
	free(currentTime);
	free(deviceId);
	freeParameterValueList(parameterList); 
	freeEventList(event);
	return inform;
}

/* H sunarthsh auth xeirizetai th sundesh me kapoion ACS server. Ws
 * parametro dexetai ena socket sto opoio uparxei mia energh sundesh
 * me ton server. */
int handleConnection(SOCKET sock) {
	unsigned int i=0;
	int r=0;
	unsigned int servermaxenvs=0; // O ari9mos twn fakelwn tou server
	int endTransaction=0; // 9a ginei 1 otan prepei na lhjei h dosolhyia
	Queue *pendingRequests = createQueue(); // Mia oura me ta requests tou server
	SoapMessage *msg1 = NULL ;
	if(pendingRequests == NULL) {
		perror("malloc cannot create the pending requests queue");
		exit(0);
	}

	/* Kat' arxhn 9a prepei na stalei to inform request. To mono
	 * shmantiko stoixeio pou fainetai na exei twra to inform einai to
	 * maxenvelopes pou ka9orizei ton ari9mo twn mhnumatwn pou mporei
	 * na dextei o client. */
	IXML_Document *doc1 = createCPEInform(); 
	StringList *m = newStringList(1);
	m->strings[0] = (char *)ixmlPrintNode( (IXML_Node*) doc1);
	sendRawMessage(sock, m);

	// Twra lamvanontai ta mhnumata apo ton server. 
	StringList *messages = getMessages(sock);
	if(messages->size == 0) {
		goto informresponseerror;
	}

	
	// Prepei to prwto mhnuma na einai ena InformResponse
	msg1 = parseStart(messages->strings[0]);
	if( msg1!=NULL && msg1->type==INFORMRESPONSE) {
		r = parseInformResponse(msg1->message, &servermaxenvs);
		if(r!=0) {
			PRINTD("Error %d while trying to parse the InformResponse message.\n", -r);
			goto informresponseerror;
		}
		PRINTD("O server dexetai to polu %d RPC sto idio mhnuma.\n", servermaxenvs);
	} else {
		// Edw 9a er9oume an den er9ei swsta to InformResponse mhnuma apo ton server
		// opote kai o pelaths 9a kleisei th sundesh. Xrhsimopoieitai ena label kai
		// h entolh goto gia aplothta: Anti tou goto 9a mporousan na epanalhg9oun oi
		// 5 grammes pou akolou9oun to label.
informresponseerror:
		PRINTD("O Client den elave ena mhnuma InformResponse.\n");
		PRINTD("Kleinei h sundesh.\n");
		mySocketClose(sock);
		return 0;
	}

	/* Sthn oura pendingRequests 9a uparxoun ta requests tou server pou den exoun akoma
	 * ejuphreth9ei. An auth h oura einai adeia se kapoio shmeio tote o client 9a stellei
	 * ena adeio http post.  */

	// Arxika prosti9entai sthn oura ta mhnumata pou lhf9hkan arxika, afou
	// o server, ektos apo to InformResponse mporei na eixe steilei kai alla
	// mhnumata
	for(i=1;i<messages->size;i++) enque(strdup(messages->strings[i]), pendingRequests);
	freeStringList(messages);


	while( endTransaction == 0) {
		if(isEmpty(pendingRequests) ) {
			PRINTD("H oura einai adeia, etsi stelletai ena adeio mhnuma.\n");
			StringList *empty = newStringList(0);
			sendRawMessage(sock, empty);
			freeStringList(empty);
		} else {
			// To num 9a isoutai eite me ton ari9mo mhnumatwn pou den exoun ejuphreth9ei
			// eite me ton ari9mo twn fakelwn tou server, analoga me to poio ek twn duo
			// einai megalutero.
			unsigned int num=(pendingRequests->size > servermaxenvs)?servermaxenvs:pendingRequests->size;
			
			// Sth lista msgs 9a apostaloun ta mhnumata pou prepei na stalloun ston server
			StringList *msgs = newStringList(num);
			if(msgs==NULL) {
				perror("malloc");
				exit(-1);
			}

			for(i=0;i<num;i++) {
				// To req einai to mhnuma pou exei seira na ejuphreth9ei 
				char *req = deque(pendingRequests);
				// To res einai h apanthsh sto mhnuma auto
				char *res = respondTo(req);
				if(res == NULL) {
					PRINTD("Problem while trying to respond to %s", req);
					PRINTD("The connection will close");
					free(req);
					freeStringList(msgs);
					endTransaction = 1 ;
					goto out;
				}
				free(req);

				// To res prosti9etai sth lista me ta nhmunata pou 9a stalloun
				msgs->strings[i] = strdup(res);
				free(res);
			}

			// Edw pleon stellontai ta mhnumata
			sendRawMessage(sock,msgs);
			freeStringList(msgs);
		}

		// Twra, lamvanontai ta mhnumata pou esteile o server
		StringList *messages = getMessages(sock); 
		if(messages->size == 0) {
			if( isEmpty(pendingRequests) ) {
				PRINTD("Lavame keno mhnuma kai den uphrxan pending requests ");
				PRINTD("apo to server, ara to transaction 9a lhjei.\n");
				endTransaction = 1;
			} else {
				PRINTD("Lavame men keno mhnuma, uparxoun omws pending requests ");
				PRINTD("kai etsi de 9a kanoume tipota. \n");
			}
		} else {
			// An lavame kapoio mhnuma tote to pros9etoume sthn oura kai arxizoume
			// pali apo thn arxh to while
			for(i=0;i<messages->size;i++) enque(strdup(messages->strings[i]), pendingRequests);
			
		}
		freeStringList(messages);

	}
out: // Edw 9a er9oume otan dhmiourgh9ei kapoio sovaro provlhma to opoio
	// o client den mporei na xeiristei, gia paradeigma an mas er9ei kapoio
	// mhnuma to opoio den einai SOAP. Se mia tetoia periptwsh de 9a 
	// apanthsoume me ena fault response, alla 9a kleisoume th sundesh, afou
	// profanws o server den einai katallhlos gia to TR69. Epishs, edw f9anoume
	// otan h sundesh lhjei (xwris jump).
	mySocketClose(sock);
	free(pendingRequests);
	return 0;
}


int handleConnectionSSL(SSL* ssl) {
	unsigned int i=0;
	int r=0;
	unsigned int servermaxenvs=0; // O ari9mos twn fakelwn tou server
	int endTransaction=0; // 9a ginei 1 otan prepei na lhjei h dosolhyia
	Queue *pendingRequests = createQueue(); // Mia oura me ta requests tou server
	SoapMessage *msg1 = NULL ;
	if(pendingRequests == NULL) {
		perror("malloc cannot create the pending requests queue");
		exit(0);
	}

	/* Kat' arxhn 9a prepei na stalei to inform request. To mono
	 * shmantiko stoixeio pou fainetai na exei twra to inform einai to
	 * maxenvelopes pou ka9orizei ton ari9mo twn mhnumatwn pou mporei
	 * na dextei o client. */
	IXML_Document *doc1 = createCPEInform(); 
	StringList *m = newStringList(1);
	m->strings[0] = (char *)ixmlPrintNode( (IXML_Node*) doc1);
	sendRawMessageSSL(ssl, m);

	// Twra lamvanontai ta mhnumata apo ton server. 
	StringList *messages = getMessagesSSL(ssl);
	if(messages->size == 0) {
		goto informresponseerror;
	}

	
	// Prepei to prwto mhnuma na einai ena InformResponse
	msg1 = parseStart(messages->strings[0]);
	if( msg1!=NULL && msg1->type==INFORMRESPONSE) {
		r = parseInformResponse(msg1->message, &servermaxenvs);
		if(r!=0) {
			PRINTD("Error %d while trying to parse the InformResponse message.\n", -r);
			goto informresponseerror;
		}
		PRINTD("O server dexetai to polu %d RPC sto idio mhnuma.\n", servermaxenvs);
	} else {
		// Edw 9a er9oume an den er9ei swsta to InformResponse mhnuma apo ton server
		// opote kai o pelaths 9a kleisei th sundesh. Xrhsimopoieitai ena label kai
		// h entolh goto gia aplothta: Anti tou goto 9a mporousan na epanalhg9oun oi
		// 5 grammes pou akolou9oun to label.
informresponseerror:
		PRINTD("O Client den elave ena mhnuma InformResponse.\n");
		PRINTD("Kleinei h sundesh.\n");
		SSL_shutdown(ssl);
		SSL_clear(ssl);
		SSL_free(ssl);
		return 0;
	}

	/* Sthn oura pendingRequests 9a uparxoun ta requests tou server pou den exoun akoma
	 * ejuphreth9ei. An auth h oura einai adeia se kapoio shmeio tote o client 9a stellei
	 * ena adeio http post.  */

	// Arxika prosti9entai sthn oura ta mhnumata pou lhf9hkan arxika, afou
	// o server, ektos apo to InformResponse mporei na eixe steilei kai alla
	// mhnumata
	for(i=1;i<messages->size;i++) enque(strdup(messages->strings[i]), pendingRequests);
	freeStringList(messages);


	while( endTransaction == 0) {
		if(isEmpty(pendingRequests) ) {
			PRINTD("H oura einai adeia, etsi stelletai ena adeio mhnuma.\n");
			StringList *empty = newStringList(0);
			sendRawMessageSSL(ssl, empty);
			freeStringList(empty);
		} else {
			// To num 9a isoutai eite me ton ari9mo mhnumatwn pou den exoun ejuphreth9ei
			// eite me ton ari9mo twn fakelwn tou server, analoga me to poio ek twn duo
			// einai megalutero.
			unsigned int num=(pendingRequests->size > servermaxenvs)?servermaxenvs:pendingRequests->size;
			
			// Sth lista msgs 9a apostaloun ta mhnumata pou prepei na stalloun ston server
			StringList *msgs = newStringList(num);
			if(msgs==NULL) {
				perror("malloc");
				exit(-1);
			}

			for(i=0;i<num;i++) {
				// To req einai to mhnuma pou exei seira na ejuphreth9ei 
				char *req = deque(pendingRequests);
				// To res einai h apanthsh sto mhnuma auto
				char *res = respondTo(req);
				if(res == NULL) {
					PRINTD("Problem while trying to respond to %s", req);
					PRINTD("The connection will close");
					free(req);
					freeStringList(msgs);
					endTransaction = 1 ;
					goto out;
				}
				free(req);

				// To res prosti9etai sth lista me ta nhmunata pou 9a stalloun
				msgs->strings[i] = strdup(res);
				free(res);
			}

			// Edw pleon stellontai ta mhnumata
			sendRawMessageSSL(ssl,msgs);
			freeStringList(msgs);
		}

		// Twra, lamvanontai ta mhnumata pou esteile o server
		StringList *messages = getMessagesSSL(ssl); 
		if(messages->size == 0) {
			if( isEmpty(pendingRequests) ) {
				PRINTD("Lavame keno mhnuma kai den uphrxan pending requests ");
				PRINTD("apo to server, ara to transaction 9a lhjei.\n");
				endTransaction = 1;
			} else {
				PRINTD("Lavame men keno mhnuma, uparxoun omws pending requests ");
				PRINTD("kai etsi de 9a kanoume tipota. \n");
			}
		} else {
			// An lavame kapoio mhnuma tote to pros9etoume sthn oura kai arxizoume
			// pali apo thn arxh to while
			for(i=0;i<messages->size;i++) enque(strdup(messages->strings[i]), pendingRequests);
			
		}
		freeStringList(messages);

	}
out: // Edw 9a er9oume otan dhmiourgh9ei kapoio sovaro provlhma to opoio
	// o client den mporei na xeiristei, gia paradeigma an mas er9ei kapoio
	// mhnuma to opoio den einai SOAP. Se mia tetoia periptwsh de 9a 
	// apanthsoume me ena fault response, alla 9a kleisoume th sundesh, afou
	// profanws o server den einai katallhlos gia to TR69. Epishs, edw f9anoume
	// otan h sundesh lhjei (xwris jump).
	SSL_shutdown(ssl);
	SSL_clear(ssl);
	SSL_free(ssl);
	free(pendingRequests);
	return 0;
}



int handleSetParameterValues(ParameterValueList *parameterList) {
	unsigned int i,j,k=0;
		
	int *errors = (int *)malloc(parameterList->size*sizeof(int) );

	// De xreiazetai autos o elegos, afou exei elex9ei h lista prin
	// thn klhsh auths ths sunarthshs.
	//if(checkSetParameterValues(parameterList, errors)!=0) {
	//error		
	//}

	int *grouparray = (int *)malloc(parameterList->size*sizeof(int) );
	for(i=0;i<parameterList->size;i++) grouparray[i]=-1;


	for(i=0;i<parameterList->size;i++) {
		if(grouparray[i]!=-1) continue;
		StateVariable *sv=getStateVariable(parameterList->parameters[i].name );
		if(sv==NULL) return -1; // This error has to be found earlier
		grouparray[i]=k;
		for(j=i+1;j<parameterList->size;j++) {
			StateVariable *tmp=getStateVariable(parameterList->parameters[j].name );
			if(tmp==NULL) return -1; // This error has to be found earlier
			if(tmp->setfun == sv->setfun) grouparray[j]=k;
		}
		k++;
	}

	for(i=0;i<k;i++) {
		int m=0;
		unsigned int groupsize = 0;
		for(j=0;j<parameterList->size;j++) if(grouparray[j]==(int)i) groupsize++;
		
		ParameterValueList * pvl = newParameterValueList(groupsize);

		for(j=0;j<parameterList->size;j++) if(grouparray[j]==(int)i) {
			pvl->parameters[m].name=strdup(parameterList->parameters[j].name);
			pvl->parameters[m].value=strdup(parameterList->parameters[j].value);
			m++;
		}
		j=0;
		while(grouparray[j]!=(int)i) j++;
		StateVariable *tmp=getStateVariable(parameterList->parameters[j].name );

		tmp->setfun(pvl);
		
		freeParameterValueList(pvl);
	}
	
	free(grouparray);

	return 0;
}

int handleGetParameterValues(ParameterValueList *parameterList) {
	unsigned int i,j,k=0;
	/* To grouparray einai enas pinakas apo int me tosa stoixeia osa
	exei kai to parameterList kai prokeitai na apo9hkeutei se auto
	o ari9mos tou group sto opoio anhkei h antistoixh parametros. 
	Arxika, ta stoixeia tou grouparray arxikopoiountai sto -1.*/
	int *grouparray = (int *)malloc(parameterList->size*sizeof(int) );
	for(i=0;i<parameterList->size;i++) grouparray[i]=-1;

	/* Me to for auto ginetai h ana9esh twn parametrwn se group. H
	ergasia xwrizetai se ena ejwteriko kai ena eswteriko for loop. O
	Ejwterikos vroxos, pairnei mia mia tis parametrous apo th list kai
	elegxei an autes anhkoun hdh se kapoio group (an dld to antistoixo
	stoixeio tou grouparray einai diaforo tou -1). An oi sunarthseis
	anhkoun se group de xreiazetai na ginei tipota opote sunexizoume
	sthn epomenh parametro. An omws h parametros den anhkei se kapoio
	group tote anati9etai h parametros sto group k. To k einai mia 
	metavlhth pou krataei ton ari9mo twn group, exei arxikh timh 0 kai
	aujanei ka9e fora pou pername se neo group. Sth sunexeia pername sto
	eswteriko for. Edw, ejetazontai oles oi parametroi apo thn trexousa
	kai pera, kai an vre9ei kapoia me idio get function me thn trexousa
	tote topo9etoume th nea parametro sto group k. Afou teleiwsei kai
	autos o elegxos to k aujanetai mias kai oles oi parametroi pou 
	anhkoun sto group auto exoun shmeiw9ei. */
	for(i=0;i<parameterList->size;i++) {
		if(grouparray[i]!=-1) continue;
		StateVariable *sv=getStateVariable(parameterList->parameters[i].name );
		if(sv==NULL) { //Error, cannot find this variable
			free(grouparray);
			return -INVALID_PARAMETER_NAME; 
		}
		grouparray[i]=k;
		for(j=i;j<parameterList->size;j++) {
			StateVariable *tmp=getStateVariable(parameterList->parameters[j].name );
			if(tmp==NULL) { //Error, cannot find this variable
				free(grouparray);
				return -INVALID_PARAMETER_NAME; 
			}
			if(tmp->getfun == sv->getfun) grouparray[j]=k;
		}
		k++;
	}

	/* Me to for auto kalountai me ton swsto tropo oi get functions gia tis
	parametrous. Parathroume oti auto to for 9a epanalhf9ei k fores, osa einai
	kai ta group parametrwn pou exoun dhmiourgh9ei sto prohgoumeno vhma. */
	for(i=0;i<k;i++) {
		int m=0;
		unsigned int groupsize = 0;
		/* Arxika, gia to i group, elegxontai oles oi parametroi. An h parametros
		j anhkei sto i group tote aujanei kata 1 h timh ths metavlhths groupsize,
		h opoia kai ka9orizei ton ari9mo twn parametrwn pou anhkoun sto group i. */
		for(j=0;j<parameterList->size;j++) if(grouparray[j]==(int)i) groupsize++;
		
		/* Sth sunexeia, dhmiourgeitai mia nea lista parametrwn, h params. Auth exei
		mege9os iso me to group size kai se authn antigrafetai to onoma ka9e mias
		apo tis parametrous pou anhkoun sto group i. */
		ParameterValueList *params = newParameterValueList(groupsize);
		for(j=0;j<parameterList->size;j++) if(grouparray[j]==(int)i) {
			params->parameters[m].name = strdup(parameterList->parameters[j].name);
			m++;
		}

		/* Edw, xrhsimopoiwntas th metavlhth j ws deikth, vriskoume thn prwth
		metavlhth pou anhkei sto group i (opoiadhpote metavlhth pou anhkei sto
		group i fusika 9a mas arkouse, kai kaloume thn getfun gia auth th metavlhth, 
		pernwntas ths ws parametro thn params, thn opoia dhmiourghsame sto prohgoumeno 
		vhma. Etsi, sth getfun 9a perastei ena ParameterValueStruct sto opoio ta 
		onomata a einai ta onomata twn parametrwn pou anhkoun sto group i, enw oi 
		times 9a prepei na sumplhrw9oun apo to xrhsth. */
		j=0;
		while(grouparray[j]!=(int)i) j++;
		StateVariable *tmp=getStateVariable(parameterList->parameters[j].name );
		tmp->getfun(params);
		
		/* Se auto to for, ginetai h antistrofh ergasia apo to prohgoumeno. Ka9e
		mia apo tis times pou epestreye h getfun mesa sto params antigrafetai pisw
		sthn arxikh lista parametrwn. */
		m=0;
		for(j=0;j<parameterList->size;j++) if(grouparray[j]==(int)i) {
			parameterList->parameters[j].value=strdup(params->parameters[m].value);
			m++;
		}

		freeParameterValueList(params);
	}
	free(grouparray);
	return 0;
}

StateVariable *getStateVariable(char *name) {
	unsigned int i;
	for(i=0;i<NUMOFVARS;i++)  if(strcmp(deviceVariables[i].name,name)==0) return (deviceVariables+i);
	return NULL;
}


char *respondTo(char *req) {
	
	SoapMessage *s = parseStart( req );
	if(s == NULL) {
		PRINTD("Did not receive a soap message. ");
		return NULL;
	}
	unsigned int i;

	switch(s->type) {
		case GETPARAMETERNAMES: {
			int r=0;
			SoapHeader *sh = NULL;
			char *pathName;
			unsigned short nextLevel;
			r=parseGetParameterNames(s->message, &pathName, &nextLevel);

			if(s->header != NULL)  {
				sh = (SoapHeader *)malloc(sizeof(SoapHeader) );
				sh->id = s->header->id;
				sh->noMoreRequests = 0;
				sh->holdRequests = 0;
				free(s->header);
			}

			if(r!=0) {
				/* Epistrefetai ena fault response. To la9os pou
				mporei na sumvei se auto to shmeio einai na einai
				la9os ta arguments tou mhnumatos. */
				IXML_Document *doc = createFaultResponse(sh, -r);
				char *tmp = (char *)ixmlPrintNode( (IXML_Node*) doc);
				ixmlNode_free((IXML_Node*) doc);
				free(pathName);
				free(sh);
				return tmp;
			}
		
			IXML_Document *doc = createGetParameterNamesResponse(sh, pathName, nextLevel);
			char *tmp = (char *)ixmlPrintNode( (IXML_Node*) doc);
			ixmlNode_free((IXML_Node*) doc);
			free(sh);
			free(pathName);
			return tmp;
								
		} break;
		case GETPARAMETERVALUES: {
			int r=0;
			StringList *pnames = newStringList(0);
			//unsigned int num=0;
			SoapHeader *sh = NULL;

			r=parseGetParameterValues(s->message, pnames);
			if(s->header != NULL)  {
				sh = (SoapHeader *)malloc(sizeof(SoapHeader) );
				sh->id = s->header->id;
				sh->noMoreRequests = 0;
				sh->holdRequests = 0;
				free(s->header);
			}
			if(r!=0) {
				/* Epistrefetai ena fault response. To la9os pou
				mporei na sumvei se auto to shmeio einai na einai
				la9os ta arguments tou mhnumatos. */
				IXML_Document *doc = createFaultResponse(sh, -r);
				char *tmp = (char *)ixmlPrintNode( (IXML_Node*) doc);
				ixmlNode_free((IXML_Node*) doc);
				freeStringList(pnames);
				return tmp;
			}



			ParameterValueList *parList = newParameterValueList(pnames->size);
			if(parList == NULL) {
				perror("malloc");
				exit(-1);
			}

			for(i=0;i<pnames->size;i++) {
				parList->parameters[i].name=pnames->strings[i];
				parList->parameters[i].value=NULL;
			}

			r=handleGetParameterValues(parList);
			if(r!=0) {
				/* Epistrefetai ena fault response. To la9os pou
				mporei na sumvei se auto to shmeio einai na mhn
				uparxei kapoia apo tis state variables pou zhtountai. */
				IXML_Document *doc = createFaultResponse(sh, -r);
				char *tmp = (char *)ixmlPrintNode( (IXML_Node*) doc);
				ixmlNode_free((IXML_Node*) doc);
				freeStringList(pnames);
				freeParameterValueList(parList);
				free(sh);
				return tmp;
			} else {
				IXML_Document *doc = createGetParameterValuesResponse(sh, parList);
				char *tmp = (char *)ixmlPrintNode( (IXML_Node*) doc);
				ixmlNode_free((IXML_Node*) doc);
				freeParameterValueList(parList);
				free(sh);
				free(pnames);
				return tmp;
			}

		} break;
		case SETPARAMETERVALUES: {
			int r = 0;
			ParameterValueList *parList = newParameterValueList(0);
			char *paramKey;
			SoapHeader *sh = NULL;
			if(s->header != NULL)  {
				sh = (SoapHeader *)malloc(sizeof(SoapHeader) );
				sh->id = s->header->id;
				sh->noMoreRequests = 0;
				sh->holdRequests = 0;
				free(s->header);
			}

			r=parseSetParameterValues(s->message, parList, &paramKey);

			if(r!=0) {
				/* Epistrefetai ena fault response. To la9os pou
				mporei na sumvei se auto to shmeio einai na einai
				la9os ta arguments tou mhnumatos. */
				IXML_Document *doc = createFaultResponse(sh, -r);
				char *tmp = (char *)ixmlPrintNode( (IXML_Node*) doc);
				ixmlNode_free((IXML_Node*) doc);
				free(parList);
				free(sh);
				return tmp;
			}

			int *errors = (int *)malloc(parList->size*sizeof(int) );
			if(checkSetParameterValues(parList, errors)!=0) {
				/* Epistrefetai ena SetParameterNames fault response. To 
				la9os pou mporei na sumvei se auto to shmeio einai na einai
				la9os ta arguments tou mhnumatos. */
				IXML_Document *doc = createSetFaultResponse(sh, parList, errors);
				char *tmp = (char *)ixmlPrintNode( (IXML_Node*) doc);
				ixmlNode_free((IXML_Node*) doc);
				free(parList);
				free(errors);
				free(sh);
				return tmp;
			}

			free(errors);
		
			handleSetParameterValues(parList);
			
			IXML_Document *doc = createSetParameterValuesResponse(sh, 0);
			char *tmp = (char *)ixmlPrintNode( (IXML_Node*) doc);
			ixmlNode_free((IXML_Node*) doc);

			free(sh);
			freeParameterValueList(parList);
			return tmp;

		} break;
		
		case GETRPCMETHODS: {
			SoapHeader *sh = NULL;
			if(s->header != NULL)  {
				sh = (SoapHeader *)malloc(sizeof(SoapHeader) );
				sh->id = s->header->id;
				sh->noMoreRequests = 0;
				sh->holdRequests = 0;
				free(s->header);
			}
			
			parseGetRPCMethods(s->message);
			// Ousiastika den ginetai parse, aplws perilamvanetai h klhsh
			// gia omoiomorfia.
			IXML_Document *doc = createGetRPCMethodsResponse(sh);
			char *tmp = (char *)ixmlPrintNode( (IXML_Node*) doc);
			ixmlNode_free((IXML_Node*) doc);

			free(sh);
			return tmp;
		} break;

		case REBOOT: {
			PRINTD("\nREBOOT\n");
			SoapHeader *sh = NULL;
			char *cmdkey;
			if(s->header != NULL)  {
				sh = (SoapHeader *)malloc(sizeof(SoapHeader) );
				sh->id = s->header->id;
				sh->noMoreRequests = 0;
				sh->holdRequests = 0;
				free(s->header);
			}
			
			int r = parseReboot(s->message, &cmdkey);
			if(r!=0) {
				/* Epistrefetai ena fault response. To la9os pou
				mporei na sumvei se auto to shmeio einai na einai
				la9os ta arguments tou mhnumatos. */
				IXML_Document *doc = createFaultResponse(sh, -r);
				char *tmp = (char *)ixmlPrintNode( (IXML_Node*) doc);
				ixmlNode_free((IXML_Node*) doc);
				free(sh);
				return tmp;
			}
			doReboot = 1;
			IXML_Document *doc = createRebootResponse(sh);
			char *tmp = (char *)ixmlPrintNode( (IXML_Node*) doc);
			ixmlNode_free((IXML_Node*) doc);
	
			free(sh);
			return tmp;
		} break;
		
		case UPLOAD: {
			PRINTD("\nUPLOAD\n");
			SoapHeader *sh = NULL;
			char *cmdkey;
			char *ftype;
			char *url;
			char *uname;
			char *pwd;
			unsigned int dsecs;
			short int status;
			DateTime *stime;
			DateTime *ctime;
			if(s->header != NULL)  {
				sh = (SoapHeader *)malloc(sizeof(SoapHeader) );
				sh->id = s->header->id;
				sh->noMoreRequests = 0;
				sh->holdRequests = 0;
				free(s->header);
			}
			

			int r = parseUpload(s->message, &cmdkey, &ftype, &url, &uname, &pwd, &dsecs);
			if(r!=0) {
				/* Epistrefetai ena fault response. To la9os pou
				mporei na sumvei se auto to shmeio einai na einai
				la9os ta arguments tou mhnumatos. */
				IXML_Document *doc = createFaultResponse(sh, -r);
				char *tmp = (char *)ixmlPrintNode( (IXML_Node*) doc);
				ixmlNode_free((IXML_Node*) doc);
				free(sh);
				free(cmdkey);
				free(ftype);
				free(url);
				free(uname);
				free(pwd); 
				return tmp;
			}
			handleUpload(cmdkey, ftype, url, uname, pwd, dsecs, &status, &stime, &ctime);
			IXML_Document *doc = createUploadResponse(sh, status, stime, ctime);
			char *tmp = (char *)ixmlPrintNode( (IXML_Node*) doc);
			ixmlNode_free((IXML_Node*) doc);
			free(cmdkey);
			free(ftype);
			free(url);
			free(uname);
			free(pwd); 
			free(stime);
			free(ctime);
			free(sh); 
			return tmp;
		} break;
		
		case DOWNLOAD: {
			PRINTD("\nDOWNLOAD\n");
			SoapHeader *sh = NULL;
			char *cmdkey;
			char *ftype;
			char *url;
			char *uname;
			char *pwd;
			unsigned int fsize;
			char *tgetfname;
			unsigned int dsecs;
			char *surl;
			char *furl;
			short int status;
			DateTime *stime;
			DateTime *ctime;
			if(s->header != NULL)  {
				sh = (SoapHeader *)malloc(sizeof(SoapHeader) );
				sh->id = s->header->id;
				sh->noMoreRequests = 0;
				sh->holdRequests = 0;
				free(s->header);
			}
			

			int r = parseDownload(s->message, &cmdkey, &ftype, &url, &uname, &pwd, &fsize, &tgetfname, &dsecs, &surl, &furl);
			if(r!=0) {
				/* Epistrefetai ena fault response. To la9os pou
				mporei na sumvei se auto to shmeio einai na einai
				la9os ta arguments tou mhnumatos. */
				IXML_Document *doc = createFaultResponse(sh, -r);
				char *tmp = (char *)ixmlPrintNode( (IXML_Node*) doc);
				ixmlNode_free((IXML_Node*) doc);
				free(sh);
				free(cmdkey);
				free(ftype);
				free(url);
				free(uname);
				free(pwd);
				free(tgetfname);
				free(surl);
				free(furl);
				return tmp;
			}
			handleDownload(cmdkey, ftype, url, uname, pwd, fsize, tgetfname, dsecs, surl, furl,  &status, &stime, &ctime);
			IXML_Document *doc = createDownloadResponse(sh, status, stime, ctime);
			char *tmp = (char *)ixmlPrintNode( (IXML_Node*) doc);
			ixmlNode_free((IXML_Node*) doc);
			free(cmdkey);
			free(ftype);
			free(url);
			free(uname);
			free(pwd);
			free(tgetfname);
			free(surl);
			free(furl);
			free(stime);
			free(ctime);
			free(sh);
			return tmp;
		} break;

		case NOTIMPLEMENTEDMESSAGE: {
			SoapHeader *sh = NULL;
			
			if(s->header != NULL)  {
				sh = (SoapHeader *)malloc(sizeof(SoapHeader) );
				sh->id = s->header->id;
				sh->noMoreRequests = 0;
				sh->holdRequests = 0;
				free(s->header);
			}
			
			IXML_Document *doc = createFaultResponse(sh, METHOD_NOT_SUPPORTED );
			char *tmp = (char *)ixmlPrintNode( (IXML_Node*) doc);
			ixmlNode_free((IXML_Node*) doc);
			free(sh);
			return tmp;
		} break;

		default: {
			PRINTD("Unknown message. Control shouldn't reach this point.\n"); 
		} break;
	}
	PRINTD("Problem, returning null.\n");
	return NULL;
}


SSL_CTX *setup_client_ctx(void) {
    SSL_CTX *ctx;
 
    ctx = SSL_CTX_new(SSLv3_client_method(  ));
    if (SSL_CTX_load_verify_locations(ctx, CAFILE, CADIR) != 1) int_error("Error loading CA file and/or directory");
    if (SSL_CTX_set_default_verify_paths(ctx) != 1) int_error("Error loading default CA file and/or directory");
    SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_callback);
    SSL_CTX_set_verify_depth(ctx, 4);
    SSL_CTX_set_options(ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2);
    if (SSL_CTX_set_cipher_list(ctx, CIPHER_LIST) != 1) int_error("Error setting cipher list (no valid ciphers)");
    return ctx;
}
